package sdk.io.grpc.helloworld;

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import io.grpc.Metadata;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.ServerCall;
import io.grpc.ServerCall.Listener;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.stub.StreamObserver;
import sdk.io.grpc.helloworld.proto.HelloGrpc;
import sdk.io.grpc.helloworld.proto.HelloRequest;
import sdk.io.grpc.helloworld.proto.HelloResponse;


public class HedgingHelloWorldServer {
    private static final Logger logger = Logger.getLogger(HedgingHelloWorldServer.class.getName());

    private Server server;

    private void start() throws IOException {
      /* The port on which the server should run */
      int port = 50051;
      server = ServerBuilder.forPort(port)
          .addService(new GreeterImpl())
          .intercept(new LatencyInjectionInterceptor())
          .build()
          .start();
      logger.info("Server started, listening on " + port);
      Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
          // Use stderr here since the logger may have been reset by its JVM shutdown hook.
          System.err.println("*** shutting down gRPC server since JVM is shutting down");
          try {
            HedgingHelloWorldServer.this.stop();
          } catch (InterruptedException e) {
            e.printStackTrace(System.err);
          }
          System.err.println("*** server shut down");
        }
      });
    }

    private void stop() throws InterruptedException {
      if (server != null) {
        server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
      }
    }

    /**
     * Await termination on the main thread since the grpc library uses daemon threads.
     */
    private void blockUntilShutdown() throws InterruptedException {
      if (server != null) {
        server.awaitTermination();
      }
    }

    /**
     * Main launches the server from the command line.
     */
    public static void main(String[] args) throws IOException, InterruptedException {
      final HedgingHelloWorldServer server = new HedgingHelloWorldServer();
      server.start();
      server.blockUntilShutdown();
    }

    static class GreeterImpl extends HelloGrpc.HelloImplBase {

      @Override
      public void call(HelloRequest req, StreamObserver<HelloResponse> responseObserver) {
          HelloResponse reply = HelloResponse.newBuilder().setMessage("Hello " + req.getName()).build();
        responseObserver.onNext(reply);
        responseObserver.onCompleted();
      }
    }

    static class LatencyInjectionInterceptor implements ServerInterceptor {

      @Override
      public <HelloRequestT, HelloReplyT> Listener<HelloRequestT> interceptCall(
          ServerCall<HelloRequestT, HelloReplyT> call,
          Metadata headers, ServerCallHandler<HelloRequestT, HelloReplyT> next) {
        int random = new Random().nextInt(100);
        long delay = 0;
        if (random < 1) {
          delay = 10_000;
        } else if (random < 5) {
          delay = 5_000;
        } else if (random < 10) {
          delay = 2_000;
        }

        if (delay > 0) {
          try {
            Thread.sleep(delay);
          } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
          }
        }
        return next.startCall(call, headers);
      }
    }
}
